home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / PROGRAM / ULARN.ARJ / ULARN.TAR / ularn / shar.c < prev    next >
C/C++ Source or Header  |  1989-10-25  |  13KB  |  545 lines

  1. /*
  2. Shar puts readable text files together in a package
  3. from which they are easy to extract.  The original version
  4. was a shell script posted to the net, shown below:
  5.  
  6.     #Date: Mon Oct 18 11:08:34 1982
  7.     #From: decvax!microsof!uw-beave!jim (James Gosling at CMU)
  8.     AR=$1
  9.     shift
  10.     for i do
  11.         echo a - $i
  12.         echo "echo x - $i" >>$AR
  13.         echo "cat >$i <<'!Funky!Stuff!'" >>$AR
  14.         cat $i >>$AR
  15.         echo "!Funky!Stuff!" >>$AR
  16.     done
  17.  
  18. Rewritten by Gary Perlman/Wang Institute/Tyngsboro, MA/01879/(617) 649-9731
  19.     Many enhancements motivated by Michael Thompson.
  20.         Directory archiving motivated by Derek Zahn @ wisconsin
  21. */
  22. /*
  23.             Expanded by Phil Cordier, philc@sco.com
  24. */
  25. #include <stdio.h>
  26. #include <sys/types.h>
  27. #include <sys/stat.h>
  28. #include <sys/dir.h>
  29. #include <ctype.h>
  30.  
  31.  
  32. /* COMMANDS */
  33. #define    EXTRACT "#! /bin/sh"     /* magic exec string at shar file start */
  34. #define    PATH    "/bin:$PATH"     /* search path for programs */
  35. #define    CAT     "cat";           /* /bin/cat */
  36. #define    SED     "sed 's/^%s//'"  /* /bin/sed removes Prefix from lines */
  37. #define    MKDIR   "mkdir"          /* make a new directory */
  38. #define    CHMOD   "chmod "       /* change file protection (for executables) */
  39. #define    CHDIR   "cd"             /* change current directory */
  40. #define    TEST    "test"           /* /bin/test files */
  41. #define    ECHO    "echo"      /* echo a message to extractor */
  42.  
  43. main (argc, argv) 
  44. int argc;
  45. char **argv;    
  46. {
  47.     int     shar ();
  48.     int     optind;
  49.  
  50.     if ((optind = initial (argc, argv)) < 0)
  51.         exit (1);
  52.  
  53.     if (header (argc, argv, optind))
  54.         exit (2);
  55.  
  56.     while (optind < argc)
  57.         traverse (argv[optind++], shar);
  58.  
  59.     footer ();
  60.     exit (0);
  61. }
  62.  
  63. /*            OPTIONS            */
  64. int NOTEXT;
  65. typedef    int    lgl;
  66. #define    true    ((lgl) 1)
  67. #define    false    ((lgl) 0)
  68. int     Lastchar;   /* the last character printed */
  69. int     Ctrlcount;  /* how many bad control characters are in file */
  70.  
  71. #define    USAGE "[-abcsvz] [-p prefix] [-d delim] [-m M -n N] files > archive\n\
  72.         -a        all of -bcvz\n\
  73.         -b         extract into basenames\n\
  74.         -c        count characters\n\
  75.         -s        work silently\n\
  76.         -v        verbose\n\
  77.         -z        use original permissions\n\
  78.         -p prefix    use <prefix> instead of X\n\
  79.         -p delim    use <delim> as file delimiter\n\
  80.         -m M        archive M of N\n\
  81.         -n N        total # of archives"
  82.  
  83. #define    OPTSTRING "abcsvzp:d:m:n:"
  84.  
  85. lgl     Verbose = false;       /* provide append/extract feedback */
  86. lgl     Basename = false;      /* extract into basenames */
  87. lgl     Count = false;         /* count characters to check transfer */
  88. lgl     Silent = false;        /* turn off all verbosity */
  89. lgl    Saveperm = false;    /* use original permissions */
  90.  
  91. char    *Delim = "!FaR!OuT!";   /* put after each file */
  92. char    Filter[100] = CAT;     /* used to extract archived files */
  93. char    *Prefix = NULL;        /* line prefix to avoid funny chars */
  94. char    *M = NULL;        /* archive M of N */
  95. char    *N = NULL;        /* total # of archives */
  96.  
  97. /* returns the index of the first operand file */
  98. initial (argc, argv) 
  99. int argc;
  100. char **argv;
  101. {
  102.     int     errflg = 0;
  103.     extern    int     optind;
  104.     extern    char    *optarg;
  105.     int     C;
  106.  
  107.     NOTEXT=0;
  108.     while ((C = getopt (argc, argv, OPTSTRING)) != EOF)
  109.         switch (C) {
  110.             case 'v': Verbose = true; break;
  111.             case 'c': Count = true; break;
  112.             case 'b': Basename = true; break;
  113.             case 'd': Delim = optarg; break;
  114.             case 'z': Saveperm = true; break;
  115.             case 's': /* silent running */
  116.                 Silent = true;
  117.                 Verbose = false;
  118.                 Count = false;
  119.                 Prefix = NULL;
  120.                 break;
  121.             case 'a': /* all the options */
  122.                 Verbose = true;
  123.                 Count = true;
  124.                 Basename = true;
  125.                 Saveperm = true;
  126.                 /* fall through to set prefix */
  127.                 optarg = "X";
  128.             case 'p': sprintf (Filter, SED, Prefix = optarg);
  129.                 break;
  130.             case 'm': M = optarg;
  131.                 break;
  132.             case 'n': N = optarg;
  133.                 break;
  134.             default: errflg++;
  135.         }
  136.     if ( (M && !N) || (N && !M) ) {
  137.         fprintf(stderr, "Must give both -m and -n\n");
  138.         fflush(stderr);
  139.         exit(1);
  140.     }
  141.     if (!M && !N) {
  142.         M = "1";
  143.         N = "1";
  144.     }
  145.     if (M && N) {
  146.         if (atoi(M) <=0 || atoi(N)<=0) {
  147.             fprintf(stderr, 
  148.                 "Must give integer arguments to -m and -n\n");
  149.             fflush(stderr);
  150.             exit(1);
  151.         }
  152.     }
  153.     if (errflg || optind == argc) {
  154.         if (optind == argc)
  155.             fprintf (stderr, "shar: No input files\n");
  156.         fprintf (stderr, "usage: shar %s\n", USAGE);
  157.         fflush(stderr);
  158.         return (-1);
  159.     }
  160.     return (optind);
  161. }
  162.  
  163. header (argc, argv, optind)
  164. char    **argv;
  165. {
  166.     int     i;
  167.     lgl     problems = false;
  168.     long    clock;
  169.     char    *ctime ();
  170.     char    *getenv ();
  171.     char    *NAME = getenv ("NAME");
  172.     char    *ORG = getenv ("ORGANIZATION");
  173.  
  174.     for (i = optind; i < argc; i++)
  175.         if (access (argv[i], 4)) /* check read permission */ {
  176.             fprintf (stderr, "shar: Can't read '%s'\n", argv[i]);
  177.             fflush(stderr);
  178.             problems++;
  179.         }
  180.     if (problems) 
  181.         return (problems);
  182.  
  183. puts   (EXTRACT);
  184. puts   ("# This is a shell archive, meaning:");
  185. printf ("# 1. Remove everything above the %s line.\n", EXTRACT);
  186. puts   ("# 2. Save the resulting text in a file.");
  187. puts   ("# 3. Execute the file with /bin/sh (not csh) to create the files:");
  188.  
  189.     for (i = optind; i < argc; i++)
  190.         printf ("#\t%s\n", argv[i]);
  191.  
  192.     time (&clock);
  193.  
  194.     printf("#\n");
  195.     printf ("# This archive created: %s", ctime (&clock));
  196.  
  197.     if (NAME) 
  198.         printf ("# By:\t%s", NAME);
  199.     if (ORG) 
  200.         printf  (" (%s)", ORG);
  201.     putchar('\n');
  202.  
  203.     puts("#");
  204.     printf ("export PATH; PATH=%s\n", PATH);
  205.  
  206.     return (0);
  207. }
  208.  
  209. footer ()
  210. {
  211.     int i,n;
  212.  
  213.     n = atoi(N);
  214.  
  215.     puts ("#");
  216.  
  217.     if (n > 1) {
  218.       printf ("#\tEnd of archive %s \(of %s\)\n", M, N);
  219.       printf ("cp /dev/null ark%sisdone\n", M);
  220.     puts ("MISSING=\"\"");
  221.       printf ("for I in ");
  222.  
  223.     for (i=1; i<=n; i++) 
  224.         printf("%d ",i);
  225.     puts(" ; do");
  226.         puts ("if test ! -f ark${I}isdone ; then");
  227.         puts ("    MISSING=\"${MISSING} ${I}\"");
  228.         puts ("fi");
  229.     puts ("done");
  230.     puts ("if test \"${MISSING}\" = \"\" ; then");
  231.  
  232.         if (Silent == false) {
  233.               if (n == 2)
  234.             printf ("     %s You have unpacked both archives.\n",ECHO);
  235.               else
  236.             printf ("     %s You have unpacked all %d archives.\n",
  237.                 ECHO,n);
  238.         }
  239.  
  240.           printf ("     rm -f ark[1-9]isdone ");
  241.     if (n > 9)
  242.             puts ("ark[1-9][0-9]isdone");
  243.     else printf("\n");
  244.  
  245.         if (Silent == false) {
  246.         puts ("else");
  247.           printf ("%s You still need to unpack the following archives:\n",
  248.             ECHO);
  249.           printf ("%s ${MISSING}\n",ECHO);
  250.         }
  251.     puts ("fi");
  252.  
  253.     } /* end if n > 1 */
  254.  
  255.     puts ("## End of shell archive.");
  256.     puts ("exit 0");
  257. }
  258.  
  259. archive (input, output)
  260. char    *input, *output;
  261. {
  262.     char    buf[BUFSIZ];
  263.     FILE    *ioptr;
  264.  
  265.     if (ioptr = fopen (input, "r")) {
  266.         if (Count == true) {
  267.             Ctrlcount = 0;    /* no bad control characters so far */
  268.             Lastchar = '\n';  /* simulate line start */
  269.         }
  270.         printf ("%s << \\%s > '%s'\n", Filter, Delim, output);
  271.         if (Prefix) {
  272.             while (fgets (buf, BUFSIZ, ioptr)) {
  273.                 if (Prefix) outline (Prefix);
  274.                 outline (buf);
  275.             }
  276.         }
  277.         else copyout (ioptr);
  278.  
  279.         /* thanks to H. Morrow Long (ittvax!long) for the next fix */
  280.         if (Lastchar != '\n') /* incomplete last line */
  281.             putchar ('\n');   /* Delim MUST begin new line! */
  282.         puts (Delim);
  283.  
  284. if (Count == true && Lastchar != '\n')
  285.     printf ("%s A missing newline was added to \"'%s'\"\n", ECHO, input);
  286.  
  287. if (Count == true && Ctrlcount)
  288.     printf ("%s %d control %s may be missing from \"'%s'\"\n",
  289.         ECHO, Ctrlcount, (Ctrlcount == 1) ? "character" : "characters",
  290.              input);
  291.  
  292.         (void) fclose (ioptr);
  293.         return (0);
  294.     }
  295.     else {
  296.         fprintf (stderr, "shar: Can't open '%s'\n", input);
  297.         fflush(stderr);
  298.         return (1);
  299.     }
  300. }
  301.  
  302. /*
  303.     Copyout copies its ioptr almost as fast as possible
  304.     except that it has to keep track of the last character
  305.     printed.  If the last character is not a newline, then
  306.     shar has to add one so that the end of file delimiter
  307.     is recognized by the shell.  This checking costs about
  308.     a 10% difference in user time.  Otherwise, it is about
  309.     as fast as cat.  It also might count control characters.
  310. */
  311. #define    badctrl(c) (iscntrl (c) && !isspace (c))
  312. copyout (ioptr)
  313. register    FILE    *ioptr;
  314. {
  315.     register    int     C;
  316.     register    int     L;
  317.     register    count;
  318.  
  319.     count = Count;
  320.     while ((C = getc (ioptr)) != EOF) {
  321.         if (count == true && badctrl (C)) Ctrlcount++;
  322.         L = putchar (C);
  323.     }
  324.     Lastchar = L;
  325. }
  326.  
  327. outline (s)
  328. register    char    *s;
  329. {
  330.     if (*s) {
  331.         while (*s) {
  332.             if (Count == true && badctrl (*s)) Ctrlcount++;
  333.             putchar (*s++);
  334.         }
  335.         Lastchar = *(s-1);
  336.     }
  337. }
  338.  
  339. #define    FSIZE     statbuf.st_size
  340. shar (file, type, pos)
  341. char    *file;     /* file or directory to be processed */
  342. int     type;      /* either 'f' for file or 'd' for directory */
  343. int     pos;       /* 0 going in to a file or dir, 1 going out */
  344. {
  345.     struct    stat    statbuf;
  346.     char    *basefile = file;
  347.  
  348.     if (!strcmp (file, ".")) return;
  349.  
  350.     if (stat (file, &statbuf)) FSIZE = 0;
  351.  
  352.     if (type=='f' && pos==0 && !is_text(file)) {
  353.         fprintf(stderr, 
  354.             "shar: %s is not a text file - not archived.\n",file);
  355.         NOTEXT=1;
  356.         fflush(stderr);
  357.         return;
  358.     }
  359.  
  360.     if (Basename == true) {
  361.         while (*basefile) basefile++; /* go to end of name */
  362.         while (basefile > file && *(basefile-1) != '/') basefile--;
  363.     }
  364.  
  365.     if (pos == 0) /* before the file starts */ {
  366.         if (type == 'd') {
  367.             printf ("if %s ! -d '%s'\n", TEST, basefile);
  368.             printf ("then\n");
  369.  
  370.             if (Verbose == true)
  371.     printf ("    %s  Creating directory \"'%s'\"\n", ECHO, basefile);
  372.  
  373.             printf ("    %s '%s'\n", MKDIR, basefile);
  374.             if (Saveperm) 
  375.                 printf("%s 0%o %s\n", 
  376.                      CHMOD, statbuf.st_mode & 07777, basefile);
  377.             printf ("fi\n");
  378.  
  379.             if (Verbose == true)
  380.     printf ("%s  Entering directory \"'%s'\"\n", ECHO, basefile);
  381.  
  382.             printf ("%s '%s'\n", CHDIR, basefile);
  383.         }
  384.         else /* type == 'f' */ {
  385.             if (Verbose == true)
  386.             printf ("%s  Extracting \"'%s'\" '(%d characters)'\n",
  387.                 ECHO, basefile, FSIZE);
  388.             if (Silent == false) {
  389.                 printf ("if %s -f '%s'\n", TEST, basefile);
  390.                 puts ("then");
  391.     printf ("    %s  Will not over-write existing file \"'%s'\"\n",
  392.                     ECHO, basefile);
  393.                 puts ("else");
  394.             }
  395.             if (archive (file, basefile)) exit (-1);
  396.         }
  397.     }
  398.     else /* pos == 1, after the file is archived */ {
  399.         if (type == 'd') {
  400.             if (Verbose == true)
  401.                 printf ("%s  Done with directory \"'%s'\"\n", 
  402.                     ECHO, basefile);
  403.             printf ("%s ..\n", CHDIR);
  404.         }
  405.         /* type == 'f' (plain file) */ 
  406.         else if (!NOTEXT) {
  407.  
  408.             if (Count == true ) {
  409.         printf("if [ `wc -c %s | awk '{printf $1}'` -ne %d ]\nthen\n",
  410.             basefile, FSIZE);
  411.         printf("\t%s -n Size change in file %s: was %d, 'is '\n", 
  412.             ECHO, basefile, FSIZE);
  413.             printf("\twc -c %s | awk '{printf $1}'\n", basefile);
  414.             printf("\t%s ' characters'\nfi\n",ECHO);
  415.             }
  416.  
  417.               if (!Saveperm) {
  418.                /* if an executable file */
  419.                    if (statbuf.st_mode & S_IEXEC 
  420.                     || statbuf.st_mode & 010 
  421.                     || statbuf.st_mode & 01) {
  422.                         printf("%s ", CHMOD);
  423.                         if (statbuf.st_mode & S_IEXEC)
  424.                         putchar('u');
  425.                        if (statbuf.st_mode & 010)
  426.                         putchar('g');
  427.                         if (statbuf.st_mode & 01)
  428.                         putchar('o');
  429.                         printf("+x %s\n", basefile);
  430.                    } /* end if executable */
  431.                   } else
  432.                 printf("%s 0%o %s\n", CHMOD, 
  433.                     statbuf.st_mode & 07777, basefile);
  434.  
  435. #ifdef NEVER
  436.             /* as sent, as recieved */
  437.             if (Verbose == true ) {
  438.                 char foo[BUFSIZ], line[BUFSIZ];
  439.                 FILE *ioptr;
  440.     
  441.                 sprintf (line, "ls -l %s", file);
  442.                 ioptr = popen (line, "r");
  443.                 fgets(foo, BUFSIZ, ioptr);
  444.                 pclose (ioptr);
  445.     
  446.                 foo[strlen(foo)-1] = '\0';
  447.                 printf("%s \"As sent  : %s\"\n", ECHO, foo);
  448.                 printf("%s \"Received : `ls -l %s`\"\n",
  449.                     ECHO,file);
  450.             } /* end if verbose */
  451. #endif /* NEVER */
  452.  
  453.             if (Silent == false)
  454.                 puts ("fi   # end of overwriting check");
  455.         }/* end plain file and if !NOTEXT */
  456.  
  457.         NOTEXT=0;
  458.     } /* end else (pos == 1) */
  459. }
  460.  
  461. #ifdef MAXNAMLEN
  462.  
  463. #define    namedir(entry) (entry->d_name)
  464. #define    MAXNAME 256
  465.  
  466. #else
  467.  
  468. #define    DIR    FILE
  469. #define    MAXNAME (DIRSIZ+2)
  470. #define    opendir(path) fopen (path, "r")
  471. #define closedir(dirp) fclose (dirp)
  472.  
  473. struct direct *
  474. readdir (dirp)
  475. DIR     *dirp;
  476. {
  477.     static    struct    direct    entry;
  478.  
  479.     if (dirp == NULL) return (NULL);
  480.     for (;;) {
  481.         if (fread (&entry, sizeof (struct direct), 1, dirp) == 0) 
  482.             return (NULL);
  483.         if (entry.d_ino) return (&entry);
  484.     }
  485. }
  486.  
  487. char    *strncpy ();
  488.  
  489. char *
  490. namedir (entry)
  491. struct    direct    *entry;
  492. {
  493.     static    char    name[MAXNAME];
  494.  
  495.     return (strncpy (name, entry->d_name, DIRSIZ));
  496. }
  497.  
  498. #endif MAXNAMLEN
  499.  
  500. #define    isdir(path) (stat(path, &buf) ? 0 : (buf.st_mode&S_IFMT)==S_IFDIR)
  501.  
  502. traverse (path, func)
  503. char    *path;
  504. int     (*func) ();
  505. {
  506.     DIR     *dirp;
  507.     struct    direct    *entry;
  508.     struct    stat    buf;
  509.     int     filetype = isdir (path) ? 'd' : 'f';
  510.  
  511.     (*func) (path, filetype, 0);
  512.  
  513.     if (filetype == 'd') {
  514.       if (chdir (path) == 0) {
  515.         if (dirp = opendir (".")) {
  516.             while (entry = readdir (dirp)) {
  517.                 char    name[MAXNAME];
  518.  
  519.                 strcpy (name, namedir (entry));
  520.                 if (strcmp(name, ".") && strcmp(name, ".."))
  521.                     traverse (name, func);
  522.             }
  523.             (void) closedir(dirp);
  524.         }
  525.         (void) chdir ("..");
  526.       }
  527.     }
  528.     (*func) (path, filetype, 1);
  529. }
  530.  
  531. is_text (file)
  532. char *file;
  533. {
  534.     int    count=0;
  535.     char line[BUFSIZ];
  536.     FILE *ioptr;
  537.  
  538.     sprintf (line, "file %s | egrep -c \"text\"", file);
  539.     ioptr = popen (line, "r");
  540.     fscanf (ioptr, "%d", &count);
  541.     pclose (ioptr);
  542.  
  543.     return (count);
  544. }
  545.